#pragma once
#include "log.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include "threadpool.hpp"
#include "task.hpp"

class tcpServer
{
public:
    class threadData
    {
    public:
        threadData(int socketfd, const string &userIp, uint16_t userPort, tcpServer *ptr)
            : _socketfd(socketfd), _userIp(userIp), _userPort(userPort), _ptr(ptr)
        {
        }

    public:
        int _socketfd;
        string _userIp;
        uint16_t _userPort;
        tcpServer *_ptr;
    };

    tcpServer(uint16_t port = 10000, string ip = "0.0.0.0")
        : _ip(ip), _port(port)
    {
        init();
        threadPool<task>::getThreadPool()->start();
    }
    ~tcpServer()
    {
    }
    void init()
    {
        // socket
        _socketListen = socket(AF_INET, SOCK_STREAM, 0);
        if (_socketListen == -1)
        {
            log(ERROR, "socket create fail");
            cout << "errno: " << errno << " " << strerror(errno);
            exit(-1);
        }
        log(NOTICE, "socket suceess !");

        // 下面的代码用来让服务器重启不需要等待时间，暂时不知道原理，先用着
        int opt = 1;
        setsockopt(_socketListen, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // bind
        sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        int ret_pton = inet_pton(AF_INET, _ip.c_str(), &local.sin_addr);
        if (ret_pton == -1)
        {
            log(ERROR, "inet_pton fail");
            exit(-1);
        }
        int ret_bind = bind(_socketListen, (sockaddr *)&local, sizeof(local));
        if (ret_bind == -1)
        {
            log(ERROR, "bind fail");
            cout << "errno: " << errno << " " << strerror(errno);
            exit(-1);
        }
        log(NOTICE, "bind suceess !");

        // listen
        int ret_listen = listen(_socketListen, 10); // 第二个参数底层流的大小不要设置的太大
        if (ret_listen == -1)
        {
            log(ERROR, "listen fail");
            exit(-1);
        }
        log(NOTICE, "listen suceess !");
    }

    void serverce(int socketfd, const string &userIp, uint16_t userPort)
    {
        char getMsg[1024];
        while (true)
        {
            // 获取数据
            int n = read(socketfd, getMsg, sizeof(getMsg) - 1);
            if (n == -1)
            {
                log(WORNING, "read fail");
                break;
            }
            else if (n == 0)
            {
                cout << "[" << userIp << ":" << userPort << "] quit" << endl;
                break;
            }
            getMsg[n] = '\0';

            // cout<<getMsg<<endl;

            // 制作返回客户端的数据并返回
            string sendMsg = "[" + userIp + ":" + to_string(userPort) + "] " + getMsg;
            write(socketfd, sendMsg.c_str(), sendMsg.size());
        }
    }

    static void *routine(void *args)
    {
        pthread_detach(pthread_self());
        threadData *td = static_cast<threadData *>(args);
        td->_ptr->serverce(td->_socketfd, td->_userIp, td->_userPort);
        close(td->_socketfd);
        delete td;
    }

    void run()
    {
        // 3.使用signal等待
        // signal(SIGCHLD,SIG_IGN);
        while (true)
        {
            sockaddr_in client;
            bzero(&client, sizeof(client));
            socklen_t len = sizeof(client);
            // accept
            int socketfd = accept(_socketListen, (sockaddr *)&client, &len);
            if (socketfd == -1)
            {
                log(WORNING, "accept fail");
                exit(-1);
            }
            // log(NOTICE,"accept success !");
            //  获取用户IP与端口号
            char userIp[32] = {0};
            inet_ntop(AF_INET, &client.sin_addr, userIp, sizeof(userIp));
            uint16_t userPort = ntohs(client.sin_port);

            cout << "[" << userIp << ":" << userPort << "] get link" << endl;

            // 1.单进程,无法做到同时为多个客户端服务
            //  serverce(socketfd, userIp, userPort);
            //  close(socketfd);

            // // 2.多进程
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程
            //     close(_socketListen);
            //     if (fork() != 0)
            //         exit(-1);
            //     serverce(socketfd, userIp, userPort);
            //     close(socketfd);
            //     exit(-1); // 孙子进程也要记得退出哦
            // }
            // // 父进程
            // close(socketfd);
            // waitpid(pid, nullptr, 0);

            // // 3.多进程 使用signal函数进行等待，父进程不需要阻塞等待
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程
            //     close(_socketListen);
            //     serverce(socketfd, userIp, userPort);
            //     close(socketfd);
            //     exit(-1);//子进程要记得退出哦
            // }
            // // 父进程
            // close(socketfd);

            // 4.进程池，在run函数最开始就创建多个进程，进程竞争的接收
            // 客户端，但是需要注意对与socket的accept需要加锁访问

            // // 5.多线程
            // pthread_t tid;
            // threadData* data= new threadData(socketfd, userIp, userPort, this);
            // pthread_create(&tid, 0, routine, (void *)data);

            // 6.线程池
            task t = task(socketfd, userIp, userPort);
            threadPool<task>::getThreadPool()->push(t);
        }
    }

private:
    int _socketListen;
    uint16_t _port;
    string _ip;
};
